#pragma once

#include <iostream>
#include <string>
#include <cstdlib>
#include <cstring>
#include <unistd.h>
#include <signal.h>
#include <pthread.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#include "logMessage.hpp"

#define SOCKET_ERR 1
#define BIND_ERR 2
#define LISTEN_ERR 3
#define USE_ERR 4
#define CONNECT_ERR 5
#define FORK_ERR 6
#define WAIT_ERR 7
using std::string;

void handlerHttpRequest(int sock) {
    char buffer[1024];
    ssize_t s = read(sock, buffer, sizeof buffer - 1);
    if (s > 0) {
        std::cout << buffer << std::endl;
    }
}
class tcpServer
{
    public:
        tcpServer(uint16_t port,const string& ip="")
            :_port(port)
            ,_ip(ip){}
        ~tcpServer()
        {}
        void Init()
        {
            _LiSockFd=socket(AF_INET,SOCK_STREAM,0);
            if(_LiSockFd<0)
            {
                logMessage(FATAL,"socket() failed::%s : %d",strerror(errno),_LiSockFd);
                exit(SOCKET_ERR);
            }
            logMessage(DEBUG,"socket() success::%d",_LiSockFd);
            struct sockaddr_in local;
            memset(&local,0,sizeof(local));
            local.sin_family=AF_INET;
            local.sin_port=htons(_port);
            _ip.empty()?(local.sin_addr.s_addr=htonl(INADDR_ANY)):(inet_aton(_ip.c_str(),&local.sin_addr));
            
            if(bind(_LiSockFd,(const struct sockaddr*)&local,sizeof(local))==-1)
            {
                logMessage(FATAL,"socket() failed::%s : %d",strerror(errno),_LiSockFd);
                exit(BIND_ERR);
            }
            logMessage(DEBUG,"bind() success::%d",_LiSockFd);

            if(listen(_LiSockFd,5)==-1)
            {
                logMessage(FATAL,"listen() failed::%s : %d",strerror(errno),_LiSockFd);
                exit(LISTEN_ERR);
            }
            logMessage(DEBUG,"listen() success::%d",_LiSockFd);
        }
        void start()
        {
            while(true)
            {
                struct sockaddr_in peer;
                socklen_t peerlen=sizeof(peer);
                
                int serviceSockFd=accept(_LiSockFd,(struct sockaddr*)&peer,&peerlen);
                if(serviceSockFd==-1)
                {
                    logMessage(WARINING,"accept() failed::%s : %d",strerror(errno),_LiSockFd);
                    continue;
                }
                string peerIp=inet_ntoa(peer.sin_addr);
                uint16_t peerPort=ntohs(peer.sin_port);
                logMessage(DEBUG,"accept() success:: [%s: %d] | %d",peerIp.c_str(),peerPort,serviceSockFd);
                pid_t id = fork();
            if (id == 0) {
                close(_LiSockFd);

                if (fork() > 0)
                    exit(0);

                handlerHttpRequest(serviceSockFd);
                exit(0);
            }
            waitpid(id, nullptr, 0);

            close(serviceSockFd);
            }
        }
    private:
        uint16_t _port;
        string _ip;
        int _LiSockFd;
};
